package parser.domain;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import util.ParserUtil;

public class First {
	private Map<Symbol, Set<Symbol>> map;

	public First(Grammar gr) {
		final Set<Production> productions = gr.getProductionRules();
		Set<NonTerminalSymbol> nonTerminals = gr.getNonTerminals();
		map = new HashMap<Symbol, Set<Symbol>>();
		Map<Symbol, Set<Symbol>> prevMap = new HashMap<Symbol, Set<Symbol>>();

		initMap(gr);

		// FIRST0
//		for (NonTerminalSymbol nonTerm : nonTerminals) {
			for (Production prod : productions) {
//				if (prod.getLhs().equals(nonTerm)) {
					if (prod.getRhs().get(0) instanceof TerminalSymbol)
						map.get(prod.getLhs()).add(prod.getRhs().get(0));
				}
//			}
//		}

		int i = 0;
		do {
			i++;
			prevMap = ParserUtil.copyStepMap(map);//new HashMap<Symbol, Set<Symbol>>(map);
			for (NonTerminalSymbol nonTerm : nonTerminals) { // for Every A in N
				for (Production prod : productions) {
					if (prod.getLhs().getSymbol().equals(nonTerm.getSymbol())) {
						List<Set<Symbol>> concatSets = new ArrayList<Set<Symbol>>();
						for (Symbol y : prod.getRhs()) {
							if (prevMap.get(y).size() != 0) {
								concatSets.add(map.get(y));
							}
						}
						map.get(nonTerm).addAll(
								ParserUtil.concatSets(concatSets));
					}
				}
			}
		} while (! ParserUtil.stepEquals(map, prevMap));
	}

	private void initMap(Grammar gr) {
		for (NonTerminalSymbol nt : gr.getNonTerminals()) {
			map.put(nt, new HashSet<Symbol>());
		}

		Set<Symbol> set;
		for (TerminalSymbol t : gr.getTerminals()) {
			set = new HashSet<Symbol>();
			set.add(t);
			map.put(t, set);
		}
	}

	public Set<Symbol> getFirst(Symbol symbol) {
		return map.get(symbol);
	}

	public Set<Symbol> getFirst(List<Symbol> alpha) {
		List<Set<Symbol>> list = new ArrayList<>();
		for(Symbol s : alpha){
			list.add(getFirst(s));
		}
		return ParserUtil.concatSets(list);
	}



}
